package controleur;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Crée un point de rendez-vous qui bloque les threads jusqu'à ce que tous les
 * threads attendus l'aient rejoint.
 * 
 * @author Martin
 *
 */
public class RendezVous {
	private int nombreDeParticipants;
	private int nombreBloque;
	private AtomicInteger stepCount;
	private AtomicBoolean isPaused = new AtomicBoolean();
	private TacheSynchronisation tache;

	public RendezVous(int nombreParticipants, TacheSynchronisation tache) {
		assert nombreParticipants > 0 : "aucun participant";
		assert tache!= null: "tache nulle";
		nombreDeParticipants = nombreParticipants;
		nombreBloque = 0;
		stepCount = new AtomicInteger();
		this.tache = tache;
	}

	/**
	 * Retire un participants du rendez-vous.
	 * @return le nombre 
	 * @throws InterruptedException 
	 */
	synchronized public int retire() throws InterruptedException {
		rejoint();
		return nombreDeParticipants--;
	}

	/**
	 * Rejoint le rendez-vous. Le thread bloque jusqu'à ce que le nombre de
	 * threads attendu l'aient rejoint.
	 * 
	 * @throws InterruptedException
	 */
	synchronized public void rejoint() throws InterruptedException {
		if (nombreBloque >= nombreDeParticipants - 1 && !isPaused.get()) {
			nombreBloque = 0;
			stepCount.addAndGet(1);
			tache.execute();
			this.notifyAll();
		} else {
			nombreBloque++;
			this.wait();
		}
	}

	public int getStepCount() {
		return stepCount.get();
	}

	/**
	 * Bloque temporairement le
	 */
	public void pause() {
		isPaused.set(true);
	}

	public boolean isPaused() {
		return isPaused.get();
	}

	/**
	 * Sort de l'état pausé.
	 * @throws InterruptedException
	 */
	synchronized public void restart() throws InterruptedException {
		isPaused.set(false);
		nombreBloque = Math.max(0, --nombreBloque);
		this.notifyAll();
	}

	public long avancePasDeTemps() {
		return stepCount.incrementAndGet();
	}

	synchronized public void init(int nbParticipants) {
		nombreBloque = 0;
		nombreDeParticipants = nbParticipants;
		stepCount = new AtomicInteger(0);
		this.notifyAll();
	}

	interface TacheSynchronisation{
		void execute();
	}
}
